﻿#include "stdafx.h"
#if _QG_ENABLE_WIDGET
#include "qg/qgasv.h"
#include "qg/qgmtrl.h"
#include "qg/qgrdraw.h"
#include "qg/qgwidget.h"
#include "qg_widget_sort.h"

//////////////////////////////////////////////////////////////////////////
// 창고

namespace _widget
{
	extern qgTexture* CreateUiTexture(qgDevice* dev);
}

// 임플
class qgWidgetShedImpl : public qgWidgetShed
{
	KCH_AUTOCLASS(qgWidgetShedImpl, qgWidgetShed, "WidgetShed");

public:
	typedef tpl::VecArray<qwControl*> ControlArray;
	typedef tpl::HashTable<tpl::BlueStr<64>, qwEventFunc> EventHash;

private:
	ControlArray	_ctls;
	EventHash		_evht;

	qwControl*		_wmodal;
	qwControl*		_wfocus;
	qwControl*		_wselect;
	qwControl*		_wover;
	qwControl*		_wactive;

	kuint			_sevid[QGSEV_MAX_VALUE];

private:
	qgWidgetShedImpl()
	{
		// 이벤트
		qgStub* stub = _dev->GetStub();
		_sevid[QGSEV_LAYOUT] = stub->AddLayoutHandler(qgSevLayoutFunc(this, &qgWidgetShedImpl::_OnLayout));
		_sevid[QGSEV_MOUSEMOVE] = stub->AddMouseMoveHandler(qgSevMouseMoveFunc(this, &qgWidgetShedImpl::_OnMouseMove));
		_sevid[QGSEV_MOUSEDOWN] = stub->AddMouseDownHandler(qgSevMouseButtonFunc(this, &qgWidgetShedImpl::_OnMouseDown));
		_sevid[QGSEV_MOUSEUP] = stub->AddMouseUpHandler(qgSevMouseButtonFunc(this, &qgWidgetShedImpl::_OnMouseUp));
		_sevid[QGSEV_MOUSEDOUBLE] = stub->AddMouseDoubleHandler(qgSevMouseButtonFunc(this, &qgWidgetShedImpl::_OnMouseDouble));
		_sevid[QGSEV_MOUSEWHEEL] = stub->AddMouseWheelHandler(qgSevMouseWheelFunc(this, &qgWidgetShedImpl::_OnMouseWheel));
		_sevid[QGSEV_KEYDOWN] = stub->AddKeyDownHandler(qgSevKeyFunc(this, &qgWidgetShedImpl::_OnKeyDown));
		_sevid[QGSEV_KEYUP] = stub->AddKeyUpHandler(qgSevKeyFunc(this, &qgWidgetShedImpl::_OnKeyUp));
		_sevid[QGSEV_CHAR] = stub->AddCharHandler(qgSevCharFunc(this, &qgWidgetShedImpl::_OnChar));

		// 리소스
		RegisterResProp();
	}

	~qgWidgetShedImpl()
	{
		ClearControls();

		qgStub* stub = _dev->GetStub();
		for (ksize_t i = 0; i < K_COUNTOF(_sevid); i++)
		{
			if (_sevid[i] != 0)
				stub->RemoveHandler(_sevid[i]);
		}
	}

public:
	//
	K_OVR void Update(float adv)
	{
		if (!K_OFLAG(_insts, QGWI_ENABLE))
			return;

		if (_wmodal)
		{
			// 모달만 업데이트
			_wmodal->OnUpdate(adv);
		}
		else
		{
			kForEach(qwControl* w, _ctls)
			{
				if (w->IsVisible() && !w->OnUpdate(adv))
					break;
			}
		}
	}

	//
	K_OVR void Draw()
	{
		if (!K_OFLAG(_insts, QGWI_ENABLE | QGWI_VISIBLE))
			return;

		if (_ctls.IsHave())
		{
			// 역순으로 그림
			for (auto it = _ctls.Last();; --it)
			{
				qwControl* w = *it;

				if (w->IsVisible() && w != _wmodal)
					w->OnDraw();

				if (it == _ctls.First())
					break;
			}

			// 모달은 나중에
			if (_wmodal)
				_wmodal->OnDraw();
		}
	}

	//
	K_OVR qwControl* GetControl(const char* name)
	{
		k_return_value_if_fail(name, NULL);

		kForEach(qwControl* w, _ctls)
		{
			if (w->GetName().CompareTo(name, true) == 0)
				return w;

			if (!w->IsContainer())
				continue;

			qwIContainer* ctnr = w->GetContainer();
			w = ctnr->GetControl(name);

			if (w)
				return w;
		}

		return NULL;
	}

	//
	K_OVR qwControl* FindControl(kint id)
	{
		kForEach(qwControl* w, _ctls)
		{
			if (w->GetId() == id)
				return w;

			if (!w->IsContainer())
				continue;

			qwIContainer* ctnr = w->GetContainer();
			w = ctnr->FindControl(id);

			if (w)
				return w;
		}

		return NULL;
	}

	//
	K_OVR qwControl* GetControlNth(kint nth)
	{
		return (ksize_t)nth < (ksize_t)_ctls.Count() ? _ctls[nth] : NULL;
	}

	//
	K_OVR kint IndexControlOf(qwControl* cx)
	{
		k_return_value_if_fail(cx, -1);

		return _ctls.Contains(cx);
	}

	//
	K_OVR bool AddControl(qwControl* cx)
	{
		k_return_value_if_fail(cx, false);

		if (cx->GetShed() || cx->GetParent())
		{
			kint nth = _ctls.Contains(cx);

			if (nth >= 0)
				return false;
		}

		qgWidgetShedImpl::UnsafeAddControl(cx);

		return true;
	}

	//
	K_OVR bool RemoveControl(qwControl* cx)
	{
		k_return_value_if_fail(cx, false);
		k_return_value_if_fail(cx->GetShed() == this, false);

		return qgWidgetShedImpl::UnsafeRemoveControl(cx);
	}

	//
	K_OVR void UnsafeAddControl(qwControl* cx)
	{
		cx->SetDepth(++_last_depth);

		_ctls.Add(cx);
		qgWidgetShedImpl::SortControls(cx);

		cx->Load();
	}

	//
	K_OVR bool UnsafeRemoveControl(qwControl* cx)
	{
		kint nth = _ctls.Contains(cx);

		if (nth < 0)
			return false;

		_ctls.RemoveAt(nth);

		cx->Unload();

		return true;
	}

	//
	K_OVR void ClearControls()
	{
		kForEach(qwControl* w, _ctls)
		{
			w->SetParent(NULL);
			w->Unload();
		}

		_ctls.Clear();
	}

	//
	K_OVR void SortControls(qwControl* primary)
	{
		// 만약 primary가 0번이면 소트하지 말자, primary가 NULL이라도 상관없음
		// 이 함수를 호출하는 곳에서는 컨트롤 1개만 조작이 들어가기 때문에...
		if (primary == *_ctls.Begin())
			return;

		//
		kint count = _ctls.Count();
		_last_depth = count;

		// 먼저 소트
		// [2013-12-18 KIM] QuickSort에 문제가 있음, 해결 될 때까진 HeapSort만 쓸것
		tpl::HeapSort(_ctls.Data(), count, _widget::SortWidget());

		// 깊이 재정렬
		kForEach(qwControl* w, _ctls)
			w->SetDepth(count--);

#if 0 && _SB_DEBUG_
		char sz[260]; k_dts(NULL, 0xFF, sz, 260 - 1);
		k_mesgf("SORT %s\n", sz);

		for (kint i = 0; i < _ctls.Count(); i++)
		{
			qwControl* w = _ctls[i];
			kTypeInfo* n = w->GetTypeInfo();
			k_mesgf("SORT#%d [%d] %s\n", i, w->GetDepth(), n->name);
		}
		k_mesg("\n");
#endif
	}

	//
	K_OVR void SetCustomEvent(const char* name, const qwEventFunc& fn)
	{
		_evht.Set(name, fn);
	}

	//
	K_OVR bool RemoveCustomEvent(const char* name)
	{
		auto it = _evht.Get(name);

		if (it == _evht.Null())
			return false;
		else
		{
			_evht.Remove(it);
			return true;
		}
	}

	//
	K_OVR const qwEventFunc* GetCustomEvent(const char* name)
	{
		auto it = _evht.Get(name);

		return it == _evht.Null() ? NULL : &it->second;
	}

	//
	K_OVR void SetForeground(qwControl* w)
	{
		if (w)
		{
			w->SetVisible(true);

			w->SetDepth(++_last_depth);
			qgWidgetShedImpl::SortControls(w);
		}

		if (_ecb_foreground_changed)
			_ecb_foreground_changed(w);
	}

	//
	K_OVR void SetFocus(qwControl* w)
	{
		if (_wfocus)
			_wfocus->OnChangeFocus(false);

		_wfocus = w;

		if (w)
			w->OnChangeFocus(true);
	}

	//
	K_OVR bool SetModal(qwControl* w)
	{
		if (!w)
		{
			if (_wmodal)
			{
				_wmodal->SetVisible(false);
				SetFocus(NULL);

				if (_ecb_modal_changed)
					_ecb_modal_changed(_wmodal, false);
			}

			_wmodal = NULL;
		}
		else if (w != _wmodal)
		{
			if (!w->IsContainer())
				return false;

			_wmodal = w;

			// 가운데 위치하게 끔
			const tpl::Point wsize = w->GetBound().Size();
			int x = (_bound.Width() - wsize.x) / 2;
			int y = (_bound.Height() - wsize.y) / 2;
			w->SetBound(x, y, wsize.x, wsize.y);

			// 처음으로
			SetForeground(w);
			SetFocus(NULL);

			if (_ecb_modal_changed)
				_ecb_modal_changed(_wmodal, true);
		}

		return true;
	}

	//
	K_OVR bool TestModal(qwControl* w)
	{
		return _wmodal == w;
	}

	//
	bool _OnLayout(qgSevLayout& e)
	{
		_bound.right = e.width;
		_bound.bottom = e.height;

		return true;
	}

	//
	bool _OnMouseMove(qgSevMouseMove& e)
	{
		if (!K_OFLAG(_insts, QGWI_ENABLE | QGWI_VISIBLE))
			return true;

		bool ret = true;
		qwControl* pick = NULL;

		// 포커스(전역) -> 모달 -> 선택 순서 대로
		if (_wfocus)
		{
			if (!_wfocus->OnMouseMove(e))
				ret = false;

			if (_wfocus->TestPick(e.x, e.y))
				goto pos_skip;
		}

		if (_wmodal)
		{
			if (!_wmodal->OnMouseMove(e))
				ret = false;

			if (_wmodal->TestPick(e.x, e.y))
				goto pos_skip;

			// 모달은 내부 선택 구조를 갖고 있기에 더 이상 처리하지 않음
		}
		else
		{
			if (_wselect)
			{
				if (!_wselect->OnMouseMove(e))
					ret = false;
			}

			// 집기
			if (ret)
			{
				pick = PickMouse(e.x, e.y);

				if (pick)
				{
					if (_wover != pick)
						SetControl(pick, QGWS_OVER);

					if (_wselect != pick && !pick->OnMouseMove(e))
						ret = false;
				}
			}
		}

pos_skip:
		// 안찍혔으면서 오버가 있었다면 해제
		if (!pick)
			SetControl(NULL, QGWS_OVER);

		return true;
	}

	//
	bool _OnMouseDown(qgSevMouseButton& e)
	{
		if (!K_OFLAG(_insts, QGWI_ENABLE | QGWI_VISIBLE))
			return true;

		qwControl* pick = _wover ? _wover : PickMouse(e.x, e.y);

		if (_wfocus)
		{
			if (pick != _wfocus || !_wfocus->TestPick(e.x, e.y, true))
				SetFocus(NULL);
			else if (!_wfocus->OnMouseDown(e))
				return false;
		}

		if (_wmodal)
		{
			if (!_wmodal->TestPick(e.x, e.y, true))
				SetFocus(NULL);
			else if (!_wmodal->OnMouseDown(e))
				return false;
		}
		else if (pick)
		{
			// 액티브
			_wactive = pick;

			//
			if (e.button == QIM_LEFT || pick->IsContainer())
				SetControl(pick, QGWS_SELECT);

			pick->SetDepth(++_last_depth);
			qgWidgetShedImpl::SortControls(pick);

			// 먼저 위에 부모가 쏘고, 자식도 검사
			if (!pick->OnMouseDown(e))
				return false;
		}

		return true;
	}

	//
	bool _OnMouseUp(qgSevMouseButton& e)
	{
		if (!K_OFLAG(_insts, QGWI_ENABLE | QGWI_VISIBLE))
			return true;

		bool ret = true;

		if (_wfocus)
		{
			if (!_wfocus->OnMouseUp(e))
				ret = false;
		}
		else if (_wmodal)
		{
			if (!_wmodal->OnMouseUp(e))
				ret = false;
		}
		else
		{
			if (_wselect)
			{
				if (!_wselect->OnMouseUp(e))
					ret = false;
			}

			qwControl* pick = PickMouse(e.x, e.y);

			if (_wselect && _wselect == pick)
			{
				// 현재 선택과 놓인 위치가 같으면
				if (e.button == QIM_LEFT || pick->IsContainer())
					SetControl(NULL, QGWS_SELECT);
			}
			else
			{
				// 현재 선택과 눌린 위치가 다르다
				SetControl(NULL, QGWS_SELECT);

				if (pick)
				{
					if (!pick->OnMouseUp(e))
						ret = false;
				}
			}
		}

		return ret;
	}

	//
	bool _OnMouseDouble(qgSevMouseButton& e)
	{
		if (!K_OFLAG(_insts, QGWI_ENABLE | QGWI_VISIBLE))
			return true;

		bool ret = true;

		if (_wfocus)
		{
			if (!_wfocus->OnMouseDouble(e))
				ret = false;
		}
		else if (_wmodal)
		{
			if (!_wmodal->OnMouseDouble(e))
				ret = false;
		}
		else if (_wover)
		{
			if (!_wover->OnMouseDouble(e))
				ret = false;
		}

		return ret;
	}

	//
	bool _OnMouseWheel(qgSevMouseWheel& e)
	{
		if (!K_OFLAG(_insts, QGWI_ENABLE | QGWI_VISIBLE))
			return true;

		qwControl* pick =
			_wfocus ? _wfocus :
			_wmodal ? _wmodal :
			_wover ? _wover :
			_wselect ? _wselect : PickMouse(e.x, e.y);

		if (pick)
		{
			if (!pick->OnMouseWheel(e))
				return false;
		}

		return true;
	}

	//
	bool _OnKeyDown(qgSevKey& e)
	{
		if (!K_OFLAG(_insts, QGWI_ENABLE | QGWI_VISIBLE))
			return true;

		qwControl* pick =
			_wfocus ? _wfocus :
			_wmodal ? _wmodal :
			_wselect ? _wselect : _wactive;

		if (pick)
		{
			if (!pick->OnKeyDown(e))
				return false;
		}

		return true;
	}

	//
	bool _OnKeyUp(qgSevKey& e)
	{
		if (!K_OFLAG(_insts, QGWI_ENABLE | QGWI_VISIBLE))
			return true;

		qwControl* pick =
			_wfocus ? _wfocus :
			_wmodal ? _wmodal :
			_wselect ? _wselect : _wactive;

		if (pick)
		{
			if (!pick->OnKeyUp(e))
				return false;
		}

		return true;
	}

	//
	bool _OnChar(qgSevChar& e)
	{
		if (!K_OFLAG(_insts, QGWI_ENABLE | QGWI_VISIBLE))
			return true;

		qwControl* pick =
			_wfocus ? _wfocus :
			_wmodal ? _wmodal :
			_wselect ? _wselect : _wactive;

		if (pick)
		{
			if (!pick->OnChar(e))
				return false;
		}

		return true;
	}

	//
	qwControl* PickMouse(kint x, kint y)
	{
		kForEach(qwControl*w, _ctls)
		{
			if (w->GetInsts(QGWI_ALIVE) != QGWI_ALIVE)
				continue;

			if (w->TestPick(x, y))
				return w;
		}

		return NULL;
	}

	//
	K_OVR void SetControl(qwControl* w, qgWidgetStatus status)
	{
		switch ((ksize_t)status)
		{
			case QGWS_SELECT:
				k_return_if_fail(w != _wselect);

				if (_wselect)
					_wselect->SetStatus(_wselect == _wover ? QGWS_OVER : QGWS_NORMAL);

				_wselect = w;

				if (w)
					w->SetStatus(QGWS_SELECT);

				break;

			case QGWS_OVER:
				if (_wover && _wover != _wselect)
					_wover->SetStatus(QGWS_NORMAL);

				_wover = w;

				if (w && w != _wselect)
					w->SetStatus(QGWS_OVER);

				break;
		}
	}

	// 위젯 등록용 클래스
	struct ResInfo : public qgWidgetResource
	{
		ResInfo(const tpl::ColorValue& color, const tpl::Quad& coord, bool usetex = true)
		{
			this->usetex = usetex;
			this->coord = coord;
			this->color = color;
		}

		explicit ResInfo(const tpl::ColorValue& color, bool usetex = true)
		{
			this->usetex = usetex;
			this->coord = tpl::Quad::FillCoord();
			this->color = color;
		}

		explicit ResInfo(const tpl::Quad& coord, bool usetex = true)
		{
			this->usetex = usetex;
			this->coord = coord;
			this->color = tpl::ColorValues::White;
		}

		explicit ResInfo(bool usetex = true)
		{
			this->usetex = usetex;
			this->coord = tpl::Quad::FillCoord();
			this->color = tpl::ColorValues::White;
		}
	};

	// 이건 위젯 리소스 등록용
	void RegisterResProp()
	{
		// 텍스챠!
		qgTexture* tex = _widget::CreateUiTexture(_dev);

		_tex[QGW_CONTROL] = tex; tex->Load();
		_tex[QGW_FORM] = tex; tex->Load();
		_tex[QGW_PANEL] = NULL;
		_tex[QGW_DIVIDE] = NULL;
		_tex[QGW_LABEL] = NULL;
		_tex[QGW_BUTTON] = NULL;
		_tex[QGW_SCROLL] = tex; tex->Load();
		_tex[QGW_LIST] = tex; tex->Load();
		_tex[QGW_COMBO] = tex; tex->Load();
		_tex[QGW_PROGRESS] = NULL;
		_tex[QGW_TEXTLINE] = NULL;

		tex->Unload();

		// 기본
		_res.Add(ResInfo(tpl::ColorValue(32, 32, 32), false));
		_res.Add(ResInfo(tpl::ColorValue(30, 120, 220), false));
		_res.Add(ResInfo(tpl::ColorValue(64, 64, 64), false));
		_res.Add(ResInfo(tpl::ColorValues::Black, false));

		// DIVIDE
		_res.Add(ResInfo(tpl::ColorValue(182, 182, 182), false));
		_res.Add(ResInfo(tpl::ColorValue(80, 80, 80), false));
		_res.Add(ResInfo(tpl::ColorValue(182, 182, 182), false));
		_res.Add(ResInfo(tpl::ColorValue(80, 80, 80), false));

		// PROGRESS
		_res.Add(ResInfo(tpl::ColorValue(30, 120, 220), false));

		// SCROLL
		_res.Add(ResInfo(tpl::Quad::ConvCoord(512, 512, 0, 0, 32, 32)));
		_res.Add(ResInfo(tpl::Quad::ConvCoord(512, 512, 0, 32, 32, 32)));
		_res.Add(ResInfo(tpl::Quad::ConvCoord(512, 512, 0, 64 + 1, 32, 64 - 2)));
		_res.Add(ResInfo(tpl::Quad::ConvCoord(512, 512, 0, 128 + 1, 32, 64 - 2)));

		// LIST
		_res.Add(ResInfo(tpl::Quad::ConvCoord(512, 512, 0, 192, 128, 32)));
		_res.Add(ResInfo(tpl::Quad::ConvCoord(512, 512, 0, 224, 128, 32)));

		// COMBO
		_res.Add(ResInfo(tpl::ColorValue(32, 32, 32), false));
		_res.Add(ResInfo(tpl::Quad::ConvCoord(512, 512, 0, 32, 32, 32)));

		// TEXTLINE
		_res.Add(ResInfo(tpl::ColorValue(0, 0, 0), false));
		_res.Add(ResInfo(tpl::ColorValue(255, 255, 255, 180), false));
		_res.Add(ResInfo(tpl::ColorValue(255, 255, 255), false));
		_res.Add(ResInfo(tpl::ColorValue(127, 127, 127, 154), false));

		// PANEL
		_res.Add(ResInfo(tpl::ColorValue(32, 32, 32), true));

		// FORM
		_res.Add(ResInfo());
		_res.Add(ResInfo(tpl::Quad::ConvCoord(512, 512, 32, 0, 32, 32)));
	}
};

KIMPL_SUBCLASS_ABSTRACT(qgWidgetShed, "AbstractWidgetShed");

//
qgWidgetShed::qgWidgetShed()
: super_type()
, _insts(QGWI_VISIBLE | QGWI_ENABLE | QGWI_INPUT)
, _last_id(K_MAX_USHORT)
, _last_depth(0)
, _blend_speed(15.0f)
, _alpha_override(0)
, _res(QGWELM_MAX_VALUE)
{
	_BaseRoam(QGROAM_OBJ_1);

	const tpl::Point& size = _dev->GetStub()->GetSize();
	_bound.Set(0, 0, size.x, size.y);

	_font = qgFreeType::New();
}

//
qgWidgetShed::~qgWidgetShed()
{
	for (ksize_t i = 0; i < K_COUNTOF(_tex); i++)
		kobj_unload(_tex[i]);

	kobj_unload(_font);
}

//
void qgWidgetShed::Update(float adv)
{
}

//
void qgWidgetShed::Draw()
{
}

//
const qgWidgetResource& qgWidgetShed::GetResource(qgWidgetElement nth)
{
	return _res[nth];
}

//
kint qgWidgetShed::AddResource(const qgWidgetResource& res)
{
	_res.Add(res);

	return _res.Count() - 1;
}

//
bool qgWidgetShed::SetResource(qgWidgetElement nth, const qgWidgetResource& res)
{
	k_return_value_if_fail((ksize_t)nth < (ksize_t)_res.Count(), false);

	_res[nth] = res;

	return true;
}

//
bool qgWidgetShed::SetTexture(qgWidgetType type, qgTexture* tex)
{
	k_return_value_if_fail((ksize_t)type < QGW_MAX_VALUE, false);

	if (_tex[type] != tex)
	{
		kobj_unload(_tex[type]);
		_tex[type] = tex;
		kobj_load(tex);
	}

	return true;
}

bool qgWidgetShed::OpenTexture(qgWidgetType type, const char* filename)
{
	k_return_value_if_fail((ksize_t)type < QGW_MAX_VALUE, false);

	kobj_unload(_tex[type]);
	_tex[type] = _dev->CreateTexture(filename, QGTEXF_S2D);

	return _tex[type] != NULL;
}

//
void qgWidgetShed::DrawAnchText(qgFont* font, const char* str, const tpl::Rect& bound, const qgWidgetTextInfo& info, kint flags)
{
	tpl::Rect bnd = bound.Deflate(info.anch.x, info.anch.y, 0, 0);

	if (_alpha_override == 0)
		font->SetColor(info.color);
	else
	{
		tpl::ColorValue cc(info.color.r, info.color.g, info.color.b, _alpha_override);
		font->SetColor(cc);
	}

	font->SetSize(info.size);
	font->TextDraw(bnd, flags, str);
}

//
void qgWidgetShed::DrawFillRect(const tpl::Rect& bound, const qgWidgetResource& res, qgTexture* tex)
{
	if (_alpha_override == 0)
		_dev->DrawRect(bound, res.color, res.usetex ? tex : NULL, &res.coord);
	else
	{
		tpl::ColorValue cc(res.color.r, res.color.g, res.color.b, _alpha_override);
		_dev->DrawRect(bound, cc, res.usetex ? tex : NULL, &res.coord);
	}
}

//
void qgWidgetShed::DrawFillRect(const tpl::Rect& bound, const tpl::ColorValue& color)
{
	if (_alpha_override == 0)
		_dev->DrawRect(bound, color, NULL, NULL);
	else
	{
		tpl::ColorValue cc(color.r, color.g, color.b, _alpha_override);
		_dev->DrawRect(bound, cc, NULL, NULL);
	}
}

//
void qgWidgetShed::DrawEmptyRect(const tpl::Rect& bound, const tpl::ColorValue& color)
{
	tpl::Point pt[4] =
	{
		tpl::Point(bound.left, bound.top),
		tpl::Point(bound.right, bound.top),
		tpl::Point(bound.right, bound.bottom),
		tpl::Point(bound.left, bound.bottom),
	};
	tpl::ColorValue cc[4] = {color, color, color, color};
	_dev->DrawPolyLine(4, pt, cc);
}

//
void qgWidgetShed::DrawLine(kint x1, kint y1, kint x2, kint y2, const tpl::ColorValue& color)
{
	tpl::Point pt[2] = {tpl::Point(x1, y1), tpl::Point(x2, y2)};
	tpl::ColorValue cc[2] = {color, color};
	_dev->DrawPolyLine(2, pt, cc);
}

//
qwControl* qgWidgetShed::CreateControl(qgWidgetType type, kint id, const tpl::Rect& bound, qwIContainer* ctn)
{
	qwControl* w;

	switch ((ksize_t)type)
	{
		case QGW_DIVIDE:
			w = kobj_create(qwDivide);
			break;

		case QGW_LABEL:
			w = kobj_create(qwLabel);
			break;

		case QGW_BUTTON:
			w = kobj_create(qwButton);
			break;

		case QGW_PROGRESS:
			w = kobj_create(qwProgress);
			break;

		case QGW_SCROLL:
			w = kobj_create(qwScroll);
			break;

		case QGW_LIST:
			w = kobj_create(qwList);
			break;

		case QGW_COMBO:
			w = kobj_create(qwCombo);
			break;

		case QGW_TEXTLINE:
			w = kobj_create(qwTextLine);
			break;

		case QGW_PANEL:
			w = kobj_create(qwPanel);
			break;

		case QGW_FORM:
			w = kobj_create(qwForm);
			break;

		default:
			w = NULL;
	}

	if (w)
	{
		if (id == 0)
			id = ++_last_id;
		else if (id >= _last_id)
			_last_id = id++;
		else if (id < 0)
		{
			// id가 0보다 작으면 0으로 두고 처리하지 않는다
			// 이는 컨트롤이 다른 컨트롤 부하로 있을 때를 위한 처리
			id = 0;
		}

		w->SetId(id);
		w->SetFont(_font);
		w->SetTexture(_tex[type]);
		w->SetBound(bound);
		w->SetShed(this);
		w->OnInitialize();

		if (ctn)
			ctn->UnsafeAddControl(w);
	}

	return w;
}

//
qwControl* qgWidgetShed::AddDivide(kint id, kint x, kint y, kint size)
{
	qwDivide* c = (qwDivide*)CreateControl(QGW_DIVIDE, id, tpl::Rect(x, y, x, y + size), this);

	c->Unload();

	return c;
}

//
qwControl* qgWidgetShed::AddLabel(kint id, kint x, kint y, kint w, kint h, const char* text)
{
	qwLabel* c = (qwLabel*)CreateControl(QGW_LABEL, id, tpl::Rect(x, y, x + w, y + h), this);

	if (text)
		c->SetText(text);

	c->Unload();

	return c;
}

//
qwControl* qgWidgetShed::AddButton(kint id, kint x, kint y, kint w, kint h, const char* text)
{
	qwButton* c = (qwButton*)CreateControl(QGW_BUTTON, id, tpl::Rect(x, y, x + w, y + h), this);

	if (text)
		c->SetText(text);

	c->Unload();

	return c;
}

//
qwControl* qgWidgetShed::AddProgress(kint id, kint x, kint y, kint w, kint h, kint minv, kint maxv)
{
	qwProgress* c = (qwProgress*)CreateControl(QGW_PROGRESS, id, tpl::Rect(x, y, x + w, y + h), this);

	c->SetRange(tpl::Point(minv, maxv));

	c->Unload();

	return c;
}

//
qwControl* qgWidgetShed::AddScroll(kint id, kint x, kint y, kint h, kint size)
{
	qwScroll* c = (qwScroll*)CreateControl(QGW_SCROLL, id, tpl::Rect(x, y, x + size, y + h), this);

	c->Unload();

	return c;
}

//
qwControl* qgWidgetShed::AddList(kint id, kint x, kint y, kint w, kint h)
{
	qwList* c = (qwList*)CreateControl(QGW_LIST, id, tpl::Rect(x, y, x + w, y + h), this);

	c->Unload();

	return c;
}

//
qwControl* qgWidgetShed::AddCombo(kint id, kint x, kint y, kint w, kint h, kint droplen)
{
	qwCombo* c = (qwCombo*)CreateControl(QGW_COMBO, id, tpl::Rect(x, y, x + w, y + h), this);

	if (droplen > 0)
		c->SetDropLength(droplen);

	c->Unload();

	return c;
}

//
qwControl* qgWidgetShed::AddTextLine(kint id, kint x, kint y, kint w, kint h, const char* text)
{
	qwTextLine* c = (qwTextLine*)CreateControl(QGW_TEXTLINE, id, tpl::Rect(x, y, x + w, y + h), this);

	if (text)
		c->SetText(text);

	c->Unload();

	return c;
}

//
qwControl* qgWidgetShed::AddPanel(kint id, kint x, kint y, kint w, kint h)
{
	qwPanel* c = (qwPanel*)CreateControl(QGW_PANEL, id, tpl::Rect(x, y, x + w, y + h), this);

	c->Unload();

	return c;
}

//
qwControl* qgWidgetShed::AddForm(kint id, kint x, kint y, kint w, kint h, const char* text /*= NULL*/)
{
	qwForm* c = (qwForm*)CreateControl(QGW_FORM, id, tpl::Rect(x, y, x + w, y + h), this);

	if (text)
		c->SetText(text);

	c->Unload();

	return c;
}

//
qgWidgetShed* qgWidgetShed::New()
{
	qgWidgetShedImpl* shed = kobj_create(qgWidgetShedImpl);
	return shed;
}

//
qgWidgetShed* qgWidgetShed::New(const char* filename)
{
	qgWidgetShedImpl* shed = kobj_create(qgWidgetShedImpl);
	shed->ReadFile(filename);
	return shed;
}

#endif
